package dao

import (
	"context"
	"fmt"
	"git.huoys.com/middle-business/xds/internal/model"
	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	endpoint "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
	"github.com/envoyproxy/go-control-plane/pkg/cache"
	"github.com/go-redis/redis/v8"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes/any"
	"log"
	"os"
)

var (
	redisDao *RedisDao
)

type RedisDao struct {
	redis *redis.Client
}

func GetRedisDao() *RedisDao {
	if redisDao == nil {
		addr := os.Getenv("REDIS_ADDR")
		client := redis.NewClient(&redis.Options{
			Addr: addr,
		})
		redisDao = &RedisDao{redis: client}
	}
	return redisDao
}

func (d *RedisDao) GetConfigJson(ctx context.Context, sub *model.EnvoySubscriber) []string {

	all := d.redis.HGetAll(ctx, getConfigKey(sub.Cluster))
	result, err := all.Result()
	if err != nil {
		return nil
	}
	res := make([]string, 0)
	for _, v := range result {
		res = append(res, v)
	}
	return res
}

func (d *RedisDao) LoadResources(ctx context.Context, resourceNames []string) ([]*any.Any, error){
	var err error
	anys := make([]*any.Any, 0)
	if len(resourceNames) == 0 {
		members := d.redis.SMembers(ctx, model.ENVOY_SUBSCRIBER_KEY)
		resourceNames, err = members.Result()
		if err != nil {
			return nil, err
		}
	}
	for _, name := range resourceNames {
		all := d.redis.HGetAll(ctx, getConfigKey(name))
		result, err := all.Result()
		if err != nil {
			log.Println("[redis error] ", err.Error())
			continue
		}
		endpoints := []*endpoint.LbEndpoint{}
		for _, v := range result {
			e :=  &endpoint.Endpoint{}
			err := proto.Unmarshal([]byte(v), e)
			if err != nil {
				log.Println("Unmarshal Endpoint fail, msg: ", err.Error())
				continue
			}
			endpoints = append(endpoints, &endpoint.LbEndpoint{
				HostIdentifier: &endpoint.LbEndpoint_Endpoint{Endpoint: e},
			})
		}
		assignment := &v2.ClusterLoadAssignment{
			ClusterName: name,
			Endpoints: []*endpoint.LocalityLbEndpoints{{LbEndpoints: endpoints}},
		}
		log.Println("LoadResources endpoints: ", endpoints)
		data, err := proto.Marshal(assignment)
		if err != nil {
			log.Println("Marshal ClusterLoadAssignment fail, msg: ", err.Error())
			continue
		}
		a := &any.Any{
			TypeUrl: cache.EndpointType,
			Value:   data,
		}
		anys = append(anys, a)
	}
	return anys, nil
}

func getConfigKey(serviceName string) string {
	return fmt.Sprintf("%s_%s", model.SUBSCRIBE_EDS, serviceName)
}

